package com.github.m6d21.sso.support.interceptor;

import static com.github.m6d21.sso.support.constant.TokenConstant.KEY_TOKEN;

import com.alibaba.fastjson.JSON;
import com.github.m6d21.sso.common.util.HttpFilterHelper;
import com.github.m6d21.sso.support.enums.StatusCode;
import com.github.m6d21.sso.support.model.Response;
import com.github.m6d21.sso.support.model.SsoTokenWrapper;
import com.github.m6d21.sso.support.session.SsoSessionHolder;
import com.github.m6d21.sso.support.token.SsoTokenHelper;
import com.github.m6d21.sso.support.util.ResponseUtil;
import com.google.common.collect.Maps;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
import java.util.Optional;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

/**
 *<p>
 *单点登录拦截器
 *</p>
 *
 * @author liuyang
 * @date 2018/12/14 Fri 10:29:00
 * @since 1.0.0
 */
public class SsoLoginInterceptor extends HandlerInterceptorAdapter {

    private static final Logger LOG = LoggerFactory.getLogger(SsoLoginInterceptor.class);

    private String loginRestfulApi;
    private String loginUrl;
    private Integer tokenExpire;
    private Boolean enabled = true;


    public void setLoginRestfulApi(String loginRestfulApi) {
        this.loginRestfulApi = loginRestfulApi;
    }

    public void setLoginUrl(String loginUrl) {
        this.loginUrl = loginUrl;
    }

    public void setTokenExpire(Integer tokenExpire) {
        this.tokenExpire = tokenExpire;
    }

    public void setEnabled(Boolean enabled) {
        this.enabled = enabled;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
        //是否开启登录拦截
        if (!enabled) {
            return true;
        }
        String url = request.getRequestURI();
        if(HttpFilterHelper.isStaticResource(url)){
            return true;
        }
        boolean verify = verifyToken(request);
        if(url.equals(loginRestfulApi)){
            //是否登录过了，登录过无需重复登录
            if(verify){
//                processJsonHasLogin(request, response);
//                return false;
            }
        }else {
            //没有登录，跳转或返回没有登录
            if (!verify) {
                if (HttpFilterHelper.isHtmlRequest(request)) {
                    processHtmlNoAuth(request, response);
                } else {
                    processJsonNoAuth(request, response);
                }
                return false;
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
        ModelAndView modelAndView) throws Exception {
        super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
        throws Exception {
        SsoSessionHolder.clear();
    }

    @Override
    public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
        super.afterConcurrentHandlingStarted(request, response, handler);
    }

    private void processHtmlNoAuth(HttpServletRequest request, HttpServletResponse response) {
        try {
            response.setHeader("Content-type", "text/html");
            response.setCharacterEncoding("UTF-8");
            response.sendRedirect(loginUrl + "");
        } catch (IOException e) {
            LOG.error("重定向失败：error:{}", e);
        }
    }

    private void processJsonNoAuth(HttpServletRequest request, HttpServletResponse response) {
        try {
            Response body = ResponseUtil.fail(StatusCode.TOKEN_INVALID.getCode(), "请求失败，请登录后重试");
            response.setHeader("Content-type", "application/json;charset=UTF-8");
            response.setCharacterEncoding("UTF-8");
            PrintWriter out = response.getWriter();
            out.print(JSON.toJSONString(body));
        } catch (IOException e) {
            LOG.error("处理响应异常：error:{}", e);
        }
    }

    private void processJsonHasLogin(HttpServletRequest request, HttpServletResponse response) {
        try {
            Map<String, Object> token = Maps.newHashMap();
            token.put("token", SsoSessionHolder.getToken());
            Response body = ResponseUtil.success(token);
            response.setHeader("Content-type", "application/json;charset=UTF-8");
            response.setCharacterEncoding("UTF-8");
            PrintWriter out = response.getWriter();
            out.print(JSON.toJSONString(body));
        } catch (IOException e) {
            LOG.error("处理响应异常：error:{}", e);
        }
    }

    private boolean verifyToken(HttpServletRequest request) {
        boolean verify = false;
        String token = (String) request.getAttribute(KEY_TOKEN);

        token = (StringUtils.isBlank(token) ? request.getHeader(KEY_TOKEN) : token);
        if (StringUtils.isBlank(token)) {
            Cookie[] cookies = Optional.ofNullable(request.getCookies()).orElse(new Cookie[0]);
            for (Cookie cookie : cookies) {
                if (KEY_TOKEN.equals(cookie.getName())) {
                    token = cookie.getValue();
                    break;
                }
            }
        }

        if (StringUtils.isNotBlank(token)) {
            SsoTokenWrapper ssoTokenWrapper = SsoTokenHelper.verifyToken(token);
            if (ssoTokenWrapper != null) {
                verify = true;
                //登录信息绑定当前线程
                SsoSessionHolder.addAttributes(ssoTokenWrapper.getAttributes());
                SsoSessionHolder.addAttribute("token", ssoTokenWrapper.getToken());
                SsoSessionHolder.addAttribute("uid", ssoTokenWrapper.getUid());
                SsoSessionHolder.addAttribute("username", ssoTokenWrapper.getUsername());
                SsoSessionHolder.addAttribute("displayName", ssoTokenWrapper.getDisplayName());
                SsoSessionHolder.addAttribute("phone", ssoTokenWrapper.getPhone());
            }
        }
        return verify;
    }
}
